package com.r.pan.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.r.pan.config.AppConfig;
import com.r.pan.constant.Constants;
import com.r.pan.entity.dto.RegisterFormDTO;
import com.r.pan.entity.dto.SysSettingDTO;
import com.r.pan.entity.po.EmailCode;
import com.r.pan.entity.po.UserInfo;
import com.r.pan.enums.CodeStatus;
import com.r.pan.enums.OperateType;
import com.r.pan.enums.ResponseCode;
import com.r.pan.enums.UserStatus;
import com.r.pan.exception.BadRequestException;
import com.r.pan.exception.BusinessException;
import com.r.pan.exception.IllegalException;
import com.r.pan.mapper.EmailCodeMapper;
import com.r.pan.mapper.UserInfoMapper;
import com.r.pan.service.IEmailCodeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.r.pan.utils.RedisUtils;
import com.r.pan.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * <p>
 * 邮箱验证码 服务实现类
 * </p>
 *
 * @author r-pocky
 * @since 2024-12-16
 */
@Slf4j
@Service
public class EmailCodeServiceImpl extends ServiceImpl<EmailCodeMapper, EmailCode> implements IEmailCodeService {

    @Resource
    private AppConfig appConfig;
    @Resource
    private JavaMailSender mailSender; // 相关配置在application.yaml文件中配置了，这里直接注入
    @Resource
    private RedisUtils redisUtils;

    @Resource
    private UserInfoMapper userInfoMapper;
    @Resource
    private EmailCodeMapper emailCodeMapper;

    @Override
    public void setAllCodeUsed(String email) {
        UpdateWrapper<EmailCode> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda()
                .eq(EmailCode::getEmail, email)
                .eq(EmailCode::getStatus, CodeStatus.NOT_USED) // 查未使用的，范围减小，更新更快
                .set(EmailCode::getStatus, CodeStatus.USED);
        update(updateWrapper);
        log.debug("更新邮箱 {} 对应的所有验证码信息为已使用", email);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendCodeToEmail(String email, Integer type) {
        OperateType operateType = OperateType.of(type);
        if (operateType == null) {
            throw new BadRequestException(String.format(Constants.EXCEPTION_MESSAGE__PARAMETER_ERROR__FORMAT, "type"));
        }
        // 尝试找到该邮箱的用户信息
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(UserInfo::getEmail, email);
        UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
        log.debug("获取邮箱 {} 对应的用户信息: {}", email, userInfo);
        // 生成随机数验证码
        String code = StringUtils.randomNumbers(Constants.LENGTH__CODE);
        switch (operateType) {
            case REGISTER:
                // 注册账号
                if (userInfo != null) {
                    throw new IllegalException("该邮箱已注册");
                }
                break;
            case RESET_PASSWORD:
                // 找回密码
                if (userInfo == null) {
                    throw new IllegalException("该邮箱未注册");
                }
                break;
            default:
                break;
        }
        // 使用邮箱向邮箱发送验证码
        sendCodeToEmail(email, code, operateType);
        // 设置所有验证码都已使用
        setAllCodeUsed(email);
        // 保存验证码信息
        EmailCode emailCode = new EmailCode();
        emailCode.setEmail(email);
        emailCode.setCode(code);
        emailCode.setStatus(CodeStatus.NOT_USED);
        emailCode.setCreateTime(LocalDateTime.now());
        emailCodeMapper.insert(emailCode);
        log.debug("保存邮箱验证码信息 {}", emailCode);
    }

    @Override
    public void sendCodeToEmail(String email, String code, OperateType operateType) {
        try {
            // 创建邮件
            MimeMessage mail = mailSender.createMimeMessage();
            // 使用helper配置邮件内容
            MimeMessageHelper helper = new MimeMessageHelper(mail, true);
            // 发送人名称和收件人邮箱
            helper.setFrom(appConfig.getEmailUsername());
            helper.setTo(email);
            // 设置发送内容
            SysSettingDTO sysSetting = redisUtils.getSysSetting();
            // 标题和内容
            if (OperateType.REGISTER.equals(operateType)) {
                // 注册
                helper.setSubject(sysSetting.getMailTitleRegister());
                helper.setText(String.format(sysSetting.getMailContentRegister(), code));
            } else if (OperateType.RESET_PASSWORD.equals(operateType)) {
                // 重置密码
                helper.setSubject(sysSetting.getMailTitleResetPassword());
                helper.setText(String.format(sysSetting.getMailContentResetPassword(), code));
            }
            helper.setSentDate(new Date());
            // 使用sender发送配置好的邮件
            mailSender.send(mail);
            log.info("向邮箱 {} 发送 {} 验证码: {}", email, operateType.getDesc(), code);
        } catch (MessagingException e) {
            log.error("发送邮箱验证码失败", e);
            throw new BusinessException("邮件验证码发送失败", ResponseCode.INTERNAL_ERROR.getCode());
        }
    }

    @Override
    public void checkEmailCode(String email, String code) {
        QueryWrapper<EmailCode> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EmailCode::getEmail, email).eq(EmailCode::getCode, code);
        EmailCode emailCode = emailCodeMapper.selectOne(queryWrapper);
        if (emailCode == null || CodeStatus.USED.equals(emailCode.getStatus())) {
            throw new BadRequestException("邮箱验证码错误");
        }
        if (emailCode.getCreateTime().plusMinutes(Constants.DURATION__CODE).isBefore(LocalDateTime.now())) {
            log.warn("邮箱验证码已过期，有效时间为" + Constants.DURATION__CODE + "分钟");
            throw new BadRequestException("邮箱验证码已过期，请重新获取");
        }
        // 更新所有邮箱验证码为已使用
        setAllCodeUsed(email);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(RegisterFormDTO registerFormDTO) {
        // 查看邮箱是否注册
        QueryWrapper<UserInfo> emailWrapper = new QueryWrapper<>();
        emailWrapper.lambda().eq(UserInfo::getEmail, registerFormDTO.getEmail());
        UserInfo userWithSameEmail = userInfoMapper.selectOne(emailWrapper);
        if (userWithSameEmail != null) {
            throw new BadRequestException("该邮箱已注册");
        }
        // 用户名要唯一
        QueryWrapper<UserInfo> usernameWrapper = new QueryWrapper<>();
        usernameWrapper.lambda().eq(UserInfo::getUsername, registerFormDTO.getUsername());
        UserInfo userWithSameUsername = userInfoMapper.selectOne(usernameWrapper);
        if (userWithSameUsername != null) {
            throw new BadRequestException("该用户名已存在");
        }
        // 校验邮箱验证码是否正确
        checkEmailCode(registerFormDTO.getEmail(), registerFormDTO.getEmailCode());
        // 随机用户id
        String userId = StringUtils.randomNumbers(Constants.LENGTH__USER_ID);
        // 新增用户信息
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(userId);
        userInfo.setUsername(registerFormDTO.getUsername());
        userInfo.setEmail(registerFormDTO.getEmail());
        userInfo.setPassword(StringUtils.encodeMD5(registerFormDTO.getPassword())); // 密码md5加密
        userInfo.setStatus(UserStatus.ENABLED);
        // 获取初始化空间大小
        SysSettingDTO sysSetting = redisUtils.getSysSetting();
        userInfo.setTotalSpace(sysSetting.getInitSpace() * Constants.UNIT_CHANGE__GB_TO_BYTE);
        userInfo.setUsedSpace(0L);
        userInfo.setCreateTime(LocalDateTime.now());
        userInfoMapper.insert(userInfo);
        log.info("新增账号信息 {}", userInfo);
    }
}
